#pragma once

#include "Comm.hpp"
#include "Channel.hpp"

class Poller
{
private:

#define MAX_EPOLL_EVENTS 1024

    void Update(Channel *channel, int op)
    {
        int fd = channel->GetFD();
        struct epoll_event ev;
        ev.data.fd = fd;
        ev.events = channel->GetEvents();
        int ret = ::epoll_ctl(_epfd, op, fd, &ev);
        if (ret < 0)
        {
            LOG(ERROR, "epoll %d %s %d(%s) failed, errno: %d -> %s\n", 
                _epfd, _events_string[op].c_str(), fd, EventsToString(ev.events).c_str(), errno, strerror(errno));
            return;
        }
        LOG(DEBUG, "epoll %d %s %d(%s) success\n", _epfd, _events_string[op].c_str(), fd, EventsToString(ev.events).c_str());
    }

    bool HasChannel(Channel *channel)
    {
        auto it = _channels.find(channel->GetFD());
        if (it == _channels.end())
        {
            return false;
        }
        return true;
    }

    const std::string EventsToString(uint32_t events)
    {
        std::string sep = " | ";
        std::string eventstr;
        if (events & EPOLLIN)
            eventstr += (sep + "EPOLLIN");
        if (events & EPOLLOUT)
            eventstr += (sep + "EPOLLOUT");
        if (events & EPOLLET)
            eventstr += (sep + "EPOLLET");
        return eventstr.substr(sep.size());
    }

    const char* PrintDebug(int num)
    {
        std::string ready_fd("");
        for (int i = 0; i < num; ++i)
        {
            // LOG(DEBUG, "%d\n", _evs[i].data.fd);
            // _evs[i].data.fd += 0;
            ready_fd += (std::to_string(_evs[i].data.fd) + " ");
        }
        // LOG(DEBUG, "%s\n", ready_fd.c_str());
        return ready_fd.c_str();
    }

public:
    Poller()
    {
        _epfd = ::epoll_create(1);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll_create failed, errno: %d -> %s\n", errno, strerror(errno));
            exit(EPOLL_CREATE_ERROR);
        }
        LOG(DEBUG, "epoll_create success, _epfd: %d\n", _epfd);

        _events_string[EPOLL_CTL_ADD] = "ADD";
        _events_string[EPOLL_CTL_MOD] = "MOD";
        _events_string[EPOLL_CTL_DEL] = "DEL";
    }

    void UpdateEvent(Channel *channel)
    {
        if (!HasChannel(channel))
        {
            _channels.insert(std::make_pair(channel->GetFD(), channel));
            Update(channel, EPOLL_CTL_ADD);
        }
        else
        {
            Update(channel, EPOLL_CTL_MOD);
        }
    }

    void RemoveEvent(Channel *channel)
    {
        auto it = _channels.find(channel->GetFD());
        if (it != _channels.end())
        {
            _channels.erase(it);
        }
        Update(channel, EPOLL_CTL_DEL);
    }

    // 开始监控，返回活跃连接
    void Poll(std::vector<Channel*> *active)
    {
        int nfds = ::epoll_wait(_epfd, _evs, MAX_EPOLL_EVENTS, -1);
        if (nfds < 0)
        {
            if (errno == EINTR)
            {
                return;
            }
            LOG(ERROR, "epoll_wait error, errno: %d -> %s\n", errno, strerror(errno));
            return;
        }
        else if (nfds > 0)
        {
            LOG(DEBUG, "There are %d FD ready: %s\n", nfds, PrintDebug(nfds));
            for (int i = 0; i < nfds; ++i)
            {
                auto it = _channels.find(_evs[i].data.fd);
                assert(it != _channels.end());
                it->second->SetRevent(_evs[i].events);
                active->push_back(it->second);
            }
        }
        else
        {
            LOG(INFO, "Timeout not FD ready!\n");
        }
    }

private:
    int _epfd;
    struct epoll_event _evs[MAX_EPOLL_EVENTS];
    std::unordered_map<int, Channel*> _channels;
    std::unordered_map<int, std::string> _events_string;
};